package com.zjw.zy.viewmodel

import android.content.Intent
import android.graphics.BitmapFactory
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.reflect.TypeToken
import com.zjw.zy.base.BaseViewModel
import com.zjw.zy.base.special.FutureLiveData
import com.zjw.zy.bean.BindLoveInfo
import com.zjw.zy.bean.WsMessage
import com.zjw.zy.constant.C2CMessageInfo
import com.zjw.zy.constant.ImageSubPath
import com.zjw.zy.constant.WechatMessageSubInfo
import com.zjw.zy.coreui.fragment.wechat.WechatFragment
import com.zjw.zy.datasource.database.AppDataBase
import com.zjw.zy.ext.*
import com.zjw.zy.manager.PageManager
import com.zjw.zy.setting.*
import com.zjw.zy.utils.*
import com.zjw.zy.voice.VoiceService
import com.zjw.zy.wsmessage.IC2CMessageHandler
import io.reactivex.disposables.Disposable
import map.location.LocationManager
import remotecall.RemoteCallManager
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList

/**
 * @author     ：zhong.jw
 * @date       ：Created in 2022/12/4 23:17
 * @description：
 */
class WsMessageDispatchViewModel : BaseViewModel() {

    companion object {
        private const val TAG = "WsMessageDispatchViewModel"

        //微信消息最小显示时间间隔，10分钟
        private const val WECHAT_MESSAGE_TIME_SHOW = 1000 * 60 * 10
    }

    private val mMessages = MutableLiveData<List<WsMessage>>()
    fun getWechatMessages(): LiveData<List<WsMessage>> = mMessages


    private val mInsertMessage = FutureLiveData<WsMessage>()
    fun getWechatMessage(): LiveData<WsMessage> = mInsertMessage

    private val mWeCacheMessage = MutableLiveData<List<WsMessage>>()

    private val mBindMessage = MutableLiveData<WsMessage>()
    fun getBindMessages(): LiveData<WsMessage> = mBindMessage

    private val mPingPong = MutableLiveData(false)
    fun getPingPong(): LiveData<Boolean> {
        return mPingPong
    }

    //是否初始化
    private var isInit = false
    private var ping: Disposable? = null


    //上次接收消息最新时间点
    private var mLastWechatMessageTime = 0L

    /**
     * 注册
     * */
    fun init() {
        if (isInit) {
            LogUtils.d(TAG, "init is init")
            return
        }
        isInit = true
        requireService()?.connectService()
        requireService()?.registerMessageObserver(
            RemoteCallManager.defaultImpl().remoteCall<WsMessage> {
                onDispatchWsMessage(it)
            })
    }

    fun unInit() {
        if (!isInit) {
            return
        }
        isInit = false
    }


    /**
     *消息分发
     */
    private fun onDispatchWsMessage(it: WsMessage) {

        if (it.info.startsWith(WsMessage.CLIENT_TO_CLIENT_INFO)) {
            processC2CMessage(it)
            return
        }

        if (it.info.startsWith(WsMessage.CLIENT_TO_CLIENT_STRONG_INFO)) {
            processC2CStrongMessage(it)
            return
        }

        if (it.info.startsWith(WsMessage.WECHAT_MESSAGE_INFO)) {
            //聊天消息
            processWechatMessage(it)
            return
        }

        if (it.info.startsWith(WsMessage.WECHAT_MESSAGE_PULL_INFO)) {
            //缓存消息
            processWechatPullMessage(it)
            return
        }

        if (it.info.startsWith(WsMessage.BIND_LOVE_INFO)) {
            //绑定消息
            processBindLoveMessage(it)
            return
        }

        if (it.info.startsWith(WsMessage.CLIENT_TO_CLIENT_FAIL_INFO)) {
            mPingPong.postValue(false)
            return
        }

        if (it.info.startsWith(WsMessage.CLIENT_TO_CLIENT_STRONG_CACHE_INFO)) {
            //强消息的缓存
            processC2CStrongMessageCache(it)
            return
        }
    }

    private fun processC2CStrongMessageCache(message: WsMessage) {
        val list: List<WsMessage> = GsonUtils.fromJson(
            message.text,
            object : TypeToken<ArrayList<WsMessage>>() {}.type
        )

        for (msg in list) {
            processC2CStrongMessage(msg)
        }
    }

    /**
     * 处理端到端的强消息
     * */
    private fun processC2CStrongMessage(msg: WsMessage) {
        LogUtils.d(TAG, "processC2CStrongMessage() called with: msg = $msg")
        msg.info = msg.info.substring(WsMessage.CLIENT_TO_CLIENT_STRONG_INFO.length)


        processC2CStrongMessageInner(msg)
    }

    /**
     * 处理端到端的消息
     * */
    private fun processC2CMessage(msg: WsMessage) {
        LogUtils.d(TAG, "processC2CMessage() called with: msg = $msg")
        msg.info = msg.info.substring(WsMessage.CLIENT_TO_CLIENT_INFO.length)

        processC2CMessageInner(msg)
    }

    private fun processWechatPullMessage(msg: WsMessage) {
        val cache: ArrayList<WsMessage> =
            GsonUtils.fromJson(msg.text, object : TypeToken<ArrayList<WsMessage>>() {}.type)
        if (cache.isNotEmpty()) {
            cache.add(
                0,
                WsMessage().apply {
                    this.info = WsMessage.WECHAT_MESSAGE_INFO + WechatMessageSubInfo.TIME
                    this.date = cache[0].date
                })
            mLastWechatMessageTime = cache.last().date
        }
        mWeCacheMessage.postValue(cache)
    }

    private fun processBindLoveMessage(msg: WsMessage) {
        //绑定对象相关
        msg.castPayload(BindLoveInfo::class.java)
        mBindMessage.postValue(msg)
    }


    fun requireWechatMessages() {
        launchInIo {
            //and 缓存
            mWeCacheMessage.value?.forEach {
                it.id = null
                AppDataBase.getInstance().wechatMessageDao().insertItem(it)
            }
            //消费完缓存消息
            mWeCacheMessage.postValue(Collections.emptyList())
            val queryAll = AppDataBase.getInstance().wechatMessageDao().queryAll()
            mMessages.postValue(queryAll)
        }
    }

    private fun processWechatMessage(message: WsMessage) {
        //不在前台，首页不是wechat，都要进行通知
        if (message.isRepair() && (PageManager.getTopFragment() !is WechatFragment || !AppUtils.isMainActivityFront())) {
            LogUtils.d(TAG, "notify message  $message")
            NotificationUtils.sendWechatNotification(activity(), message)
        }

        launchInIo {
            //是否新增时间消息
            if (message.date - mLastWechatMessageTime > WECHAT_MESSAGE_TIME_SHOW) {
                WsMessage().apply {
                    this.info = WsMessage.WECHAT_MESSAGE_INFO + WechatMessageSubInfo.TIME
                    this.date = message.date
                    this.id = null
                    AppDataBase.getInstance().wechatMessageDao().insertItem(this)
                    mInsertMessage.postValue(this)
                }
            }

            //just for main key
            message.id = null

            processWechatMessageInner(message)

            mLastWechatMessageTime = message.date
        }
    }

    private fun processWechatMessageInner(message: WsMessage) {
        //获取subInfo
        val subInfo = message.info.substring(WsMessage.WECHAT_MESSAGE_INFO.length)

        when (subInfo) {
            WechatMessageSubInfo.TEXT -> {
                AppDataBase.getInstance().wechatMessageDao().insertItem(message)
                mInsertMessage.postValue(message)
            }

            WechatMessageSubInfo.IMAGE -> {
                //图片消息
                val bytes = GsonUtils.fromJson(message.text, ByteArray::class.java)
                val bitmap = fromByteArray(bytes)
                val path =
                    PathUtils.saveBitmapToPrivatePath(bitmap, ImageSubPath.WECHAT_IMAGE_MESSAGE)
                message.text = path
                AppDataBase.getInstance().wechatMessageDao().insertItem(message)
                mInsertMessage.postValue(message)
            }

        }
    }

    fun send(message: WsMessage) {
        launchInIo {
            requireService()?.sendWsMessage(message)
        }
    }

    fun convertAndSend(message: WsMessage) {
        launchInIo {
            message.jsonPayload()
            requireService()?.sendWsMessage(message)
        }
    }


    /**
     * 发送微信消息
     * */
    fun sendWechatMessage(message: String, subInfo: String = WechatMessageSubInfo.TEXT) {
        launchInIo {
            WsMessage().apply {
                from = AppUtils.getSelfAccess()
                to = AppUtils.getLoveAccess()
                text = message
                info = WsMessage.WECHAT_MESSAGE_INFO + subInfo
                date = TimeUtils.currentTime().currentTime
                processWechatMessage(this)
                send(this)
            }
        }
    }

    fun connectService() {
        launchInIo {
            requireService()?.connectService()
        }
    }

    fun startPingLove() {
        LogUtils.d(TAG, "startPingLove() called")
        ping?.dispose()
        val p = WsMessage.obtainC2C(C2CMessageInfo.C2C_PING)
        ping = io.reactivex.Observable.interval(0, 5, TimeUnit.SECONDS).subscribe {
            send(p)
        }
    }

    fun stopPingLove() {
        LogUtils.d(TAG, "stopPingLove() called")
        ping?.dispose()
    }


    private fun processC2CMessageInner(msg: WsMessage) {
        LogUtils.d(TAG, "process() called with: msg = $msg")
        when (msg.info) {
            C2CMessageInfo.QUERY_LOCATION -> {
                SettingStorage.getOption(LocationShareOption::class.java)?.let {
                    if (it.value) {
                        LocationManager.requestOnceLocation { location ->
                            WsMessage.obtainC2C(C2CMessageInfo.QUERY_LOCATION_REPAIR).apply {
                                payload = location
                                convertAndSend(this)
                            }
                        }
                    } else {
                        LogUtils.d(TAG, "reject request location")
                    }
                }
            }

            C2CMessageInfo.VOICE_CONNECT_REQUEST -> {
                AppUtils.getMainActivity().startForegroundService(Intent().apply {
                    action = VoiceService.SERVICE_ACTION
                    `package` = AppUtils.getMainActivity().packageName
                })
            }

            C2CMessageInfo.C2C_PING -> {
                val pong = WsMessage.obtainC2C(C2CMessageInfo.C2C_PONG)
                send(pong)
            }

            C2CMessageInfo.C2C_PONG -> {
                mPingPong.postValue(true)
            }


            C2CMessageInfo.QUERY_LOCATION_REPAIR -> {
                (PageManager.getTopFragment() as? IC2CMessageHandler)?.apply {
                    if (this.onInterception(msg.info)) {
                        this.onHandleC2CMessage(msg)
                    }
                }
            }
        }

    }


    private fun processC2CStrongMessageInner(msg: WsMessage) {
        when (msg.info) {
            C2CMessageInfo.WECHAT_CHANGE_BACKGROUND -> {
                LogUtils.d(TAG, "process wechat-background $msg")
                safeCall {
                    val bitmapByte = GsonUtils.fromJson(msg.text, ByteArray::class.java)
                    val bitmap = BitmapFactory.decodeByteArray(bitmapByte, 0, bitmapByte.size)
                    PathUtils.saveBitmapToPrivatePath(bitmap, ImageSubPath.WECHAT_BACKGROUND)
                        ?.let { path ->
                            SettingStorage.setOption(WechatBackgroundPathOption().apply {
                                this.value = path
                            })
                        }
                }
            }

            C2CMessageInfo.START_UP_CHANGE_BACKGROUND -> {
                LogUtils.d(TAG, "process start-up-background $msg")
                safeCall {
                    val bitmapByte = GsonUtils.fromJson(msg.text, ByteArray::class.java)
                    val bitmap = BitmapFactory.decodeByteArray(bitmapByte, 0, bitmapByte.size)
                    PathUtils.saveBitmapToPrivatePath(bitmap, ImageSubPath.START_UP_IMAGE)
                        ?.let { path ->
                            SettingStorage.setOption(StartUpImagePathOption().apply {
                                this.value = path
                            })
                        }
                }
            }


            C2CMessageInfo.LOVER_NAME_SETTING -> {
                LogUtils.d(TAG, "process LOVER_NAME_SETTING $msg")
                safeCall {
                    val name = msg.text
                    SettingStorage.setOption(SelfNameOption().apply { this.value = name })
                }
            }
        }
    }

}